@levischuck/tiny-html 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
- import { ParseResult, HtmlNode, WriterOptions } from './types.ts';
1
+ import { ParseResult, HtmlNode, WriterOptions, SafeHtmlOptions, ParserOptions } from './types.ts';
2
2
  /**
3
3
  * Parses HTML string into a ParseResult containing HtmlNode
4
+ * @param html - The HTML string to parse
5
+ * @param options - Parser options including attributeNaming ('reactName' or 'exactName')
4
6
  */
5
- export declare function readHtml(html: string): ParseResult;
7
+ export declare function readHtml(html: string, options?: ParserOptions): ParseResult;
6
8
  /**
7
9
  * Renders HtmlNode or ParseResult to HTML string
8
10
  */
@@ -12,8 +14,18 @@ export declare function writeHtml(input: HtmlNode | ParseResult, options?: Write
12
14
  * Returns a new HtmlNode with all promises resolved
13
15
  */
14
16
  export declare function awaitHtmlNode(node: HtmlNode | Promise<HtmlNode>): Promise<HtmlNode>;
17
+ /**
18
+ * Sanitizes an HtmlNode tree by removing disallowed tags, attributes, and URLs.
19
+ * - Tags not in allowedTags have their content retained (folded into parent)
20
+ * - Images replaced with their alt text when removed
21
+ * - Attributes not in the tag's allowed list are dropped
22
+ * - Classes are filtered by allowedClasses patterns (supports wildcards)
23
+ * - URLs not matching allowedLinkProtocols cause the element to be removed
24
+ */
25
+ export declare function safeHtml(node: HtmlNode, options?: SafeHtmlOptions): HtmlNode;
15
26
  export { htmlNodeTo } from './convert.ts';
16
27
  export { decodeHtmlEntities, encodeHtmlEntities } from './entities.ts';
17
28
  export type { CreateElementFn, CreateElementProps } from './convert.ts';
18
29
  export { getTextContent } from './writer.ts';
19
- export type { WriterOptions, ParseResult, HtmlNode, HtmlElement, HtmlProps, HtmlStyle } from './types.ts';
30
+ export { htmlNodeToHtmlElement } from './utils.ts';
31
+ export type { WriterOptions, ParseResult, HtmlNode, HtmlElement, HtmlProps, HtmlStyle, SafeHtmlOptions, AllowedTag, ParserOptions, ParserAttributeNaming, WriterAttributeNaming, } from './types.ts';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const F = /* @__PURE__ */ new Set([
1
+ const $ = /* @__PURE__ */ new Set([
2
2
  "area",
3
3
  "base",
4
4
  "br",
@@ -13,7 +13,7 @@ const F = /* @__PURE__ */ new Set([
13
13
  "source",
14
14
  "track",
15
15
  "wbr"
16
- ]), Q = /* @__PURE__ */ new Set([
16
+ ]), J = /* @__PURE__ */ new Set([
17
17
  // Containers
18
18
  "div",
19
19
  "span",
@@ -110,7 +110,7 @@ const F = /* @__PURE__ */ new Set([
110
110
  "body",
111
111
  "title",
112
112
  "meta"
113
- ]), q = /* @__PURE__ */ new Map([
113
+ ]), z = /* @__PURE__ */ new Map([
114
114
  ["p", /* @__PURE__ */ new Set(["p"])],
115
115
  ["li", /* @__PURE__ */ new Set(["li"])],
116
116
  ["dt", /* @__PURE__ */ new Set(["dt", "dd"])],
@@ -123,7 +123,7 @@ const F = /* @__PURE__ */ new Set([
123
123
  ["tr", /* @__PURE__ */ new Set(["tr"])],
124
124
  ["td", /* @__PURE__ */ new Set(["td", "th"])],
125
125
  ["th", /* @__PURE__ */ new Set(["td", "th"])]
126
- ]), V = /* @__PURE__ */ new Map([
126
+ ]), B = /* @__PURE__ */ new Map([
127
127
  ["altglyph", "altGlyph"],
128
128
  ["altglyphdef", "altGlyphDef"],
129
129
  ["altglyphitem", "altGlyphItem"],
@@ -161,7 +161,7 @@ const F = /* @__PURE__ */ new Set([
161
161
  ["lineargradient", "linearGradient"],
162
162
  ["radialgradient", "radialGradient"],
163
163
  ["textpath", "textPath"]
164
- ]), z = /* @__PURE__ */ new Map([
164
+ ]), V = /* @__PURE__ */ new Map([
165
165
  ["attributename", "attributeName"],
166
166
  ["attributetype", "attributeType"],
167
167
  ["basefrequency", "baseFrequency"],
@@ -220,7 +220,7 @@ const F = /* @__PURE__ */ new Set([
220
220
  ["xchannelselector", "xChannelSelector"],
221
221
  ["ychannelselector", "yChannelSelector"],
222
222
  ["zoomandpan", "zoomAndPan"]
223
- ]), I = {
223
+ ]), P = {
224
224
  lt: "<",
225
225
  gt: ">",
226
226
  amp: "&",
@@ -291,44 +291,44 @@ const F = /* @__PURE__ */ new Set([
291
291
  rArr: "⇒",
292
292
  dArr: "⇓",
293
293
  hArr: "⇔"
294
- }, B = {};
295
- for (const [e, n] of Object.entries(I))
296
- B[n] || (B[n] = e);
297
- function w(e) {
298
- return !e || typeof e != "string" || !e.includes("&") ? e : e.replace(/&(?:#[xX]([0-9a-fA-F]+)|#(\d+)|([a-zA-Z][a-zA-Z0-9]*));/g, (n, r, l, a) => {
299
- if (r) {
300
- const o = parseInt(r, 16);
301
- return String.fromCodePoint(o);
294
+ }, W = {};
295
+ for (const [e, t] of Object.entries(P))
296
+ W[t] || (W[t] = e);
297
+ function O(e) {
298
+ return !e || typeof e != "string" || !e.includes("&") ? e : e.replace(/&(?:#[xX]([0-9a-fA-F]+)|#(\d+)|([a-zA-Z][a-zA-Z0-9]*));/g, (t, i, l, s) => {
299
+ if (i) {
300
+ const a = parseInt(i, 16);
301
+ return String.fromCodePoint(a);
302
302
  } else if (l) {
303
- const o = parseInt(l, 10);
304
- return String.fromCodePoint(o);
305
- } else if (a && I[a])
306
- return I[a];
307
- return n;
303
+ const a = parseInt(l, 10);
304
+ return String.fromCodePoint(a);
305
+ } else if (s && P[s])
306
+ return P[s];
307
+ return t;
308
308
  });
309
309
  }
310
- function x(e, n = !1) {
310
+ function G(e, t = !1) {
311
311
  if (!e || typeof e != "string")
312
312
  return e;
313
- let r = e;
314
- return r = r.replace(/&/g, "&amp;"), r = r.replace(/</g, "&lt;"), r = r.replace(/>/g, "&gt;"), n && (r = r.replace(/"/g, "&quot;")), r;
313
+ let i = e;
314
+ return i = i.replace(/&/g, "&amp;"), i = i.replace(/</g, "&lt;"), i = i.replace(/>/g, "&gt;"), t && (i = i.replace(/"/g, "&quot;")), i;
315
315
  }
316
- function Z(e) {
316
+ function ee(e) {
317
317
  return e.replace(/\]\]>/g, "]]]]><![CDATA[>");
318
318
  }
319
- function k(e) {
320
- return e.replace(/[-_]([a-z])/g, (n, r) => r.toUpperCase());
319
+ function Z(e) {
320
+ return e.replace(/[-_]([a-z])/g, (t, i) => i.toUpperCase());
321
321
  }
322
- function $(e) {
323
- return e.replace(/[A-Z]/g, (n) => `-${n.toLowerCase()}`);
322
+ function te(e) {
323
+ return e.replace(/[A-Z]/g, (t) => `-${t.toLowerCase()}`);
324
324
  }
325
- function d(e, n, r) {
326
- return new TextDecoder("utf-8").decode(e.slice(n, r));
325
+ function b(e, t, i) {
326
+ return new TextDecoder("utf-8").decode(e.slice(t, i));
327
327
  }
328
- function S(e) {
328
+ function C(e) {
329
329
  return e === 32 || e === 9 || e === 10 || e === 13;
330
330
  }
331
- function N(e) {
331
+ function S(e) {
332
332
  return e >= 97 && e <= 122 || // a-z
333
333
  e >= 65 && e <= 90 || // A-Z
334
334
  e >= 48 && e <= 57 || // 0-9
@@ -336,419 +336,615 @@ function N(e) {
336
336
  e === 95 || // _
337
337
  e === 58;
338
338
  }
339
- function K(e) {
340
- const r = new TextEncoder().encode(e), l = {
339
+ function Ae(e) {
340
+ if (typeof e == "string" && e.trim().startsWith("<") && (e = Q(e).node), typeof e == "object" && e && "type" in e)
341
+ return e;
342
+ if (Array.isArray(e)) {
343
+ for (const t of e)
344
+ if (typeof t == "object" && t && "type" in t)
345
+ return t;
346
+ }
347
+ throw new Error("Could not find HtmlElement in decoded Html");
348
+ }
349
+ function Q(e, t = {}) {
350
+ const i = t.attributeNaming ?? "reactName", s = new TextEncoder().encode(e), a = {
341
351
  node: []
342
- }, a = [], o = [];
343
- let u = "HTML", i = "TEXT", t = 0, c = 0, y = 0, A = 0, b = 0, g = "", P = 0, h = null;
344
- function M() {
345
- return o.length > 0 ? o[o.length - 1].children : a;
352
+ }, p = [], o = [];
353
+ let T = "HTML", n = "TEXT", r = 0, h = 0, g = 0, E = 0, N = 0, A = "", q = 0, d = null;
354
+ function x() {
355
+ return o.length > 0 ? o[o.length - 1].children : p;
346
356
  }
347
- function X(s, f) {
348
- if (s >= f) return;
349
- const p = d(r, s, f), T = w(p);
350
- T.length > 0 && M().push(T);
357
+ function D(f, c) {
358
+ if (f >= c) return;
359
+ const u = b(s, f, c), m = O(u);
360
+ m.length > 0 && x().push(m);
351
361
  }
352
- function U(s, f) {
353
- if (s >= f) return;
354
- const p = d(r, s, f);
355
- p.length > 0 && M().push(p);
362
+ function M(f, c) {
363
+ if (f >= c) return;
364
+ const u = b(s, f, c);
365
+ u.length > 0 && x().push(u);
356
366
  }
357
- function C(s, f) {
358
- const p = s.toLowerCase();
359
- return f === "SVG" && V.has(p) ? V.get(p) : p;
367
+ function v(f, c) {
368
+ const u = f.toLowerCase();
369
+ return c === "SVG" && B.has(u) ? B.get(u) : u;
360
370
  }
361
- function _(s, f) {
362
- const p = s.toLowerCase();
363
- if (p === "class") return "className";
364
- if (p === "for") return "htmlFor";
365
- if (f === "SVG")
366
- return z.has(p) ? z.get(p) : s;
367
- if (f === "HTML") {
368
- const T = k(p);
369
- return p === "class" || p === "for" ? p === "class" ? "className" : "htmlFor" : T;
370
- }
371
- return p;
371
+ function L(f, c) {
372
+ const u = f.toLowerCase();
373
+ return u === "class" ? i === "exactName" ? "class" : "className" : u === "for" ? i === "exactName" ? "for" : "htmlFor" : c === "SVG" ? V.has(u) ? V.get(u) : f : c === "HTML" ? Z(u) : u;
372
374
  }
373
- function R(s) {
374
- const f = C(s, u);
375
- for (let p = o.length - 1; p >= 0; p--)
376
- if (o[p].type === f) {
377
- for (let T = o.length - 1; T >= p; T--) {
378
- const m = o.pop(), E = {
379
- type: m.type,
375
+ function I(f) {
376
+ const c = v(f, T);
377
+ for (let u = o.length - 1; u >= 0; u--)
378
+ if (o[u].type === c) {
379
+ for (let m = o.length - 1; m >= u; m--) {
380
+ const y = o.pop(), w = {
381
+ type: y.type,
380
382
  props: {
381
- ...m.props,
382
- children: m.children.length === 1 ? m.children[0] : m.children
383
+ ...y.props,
384
+ children: y.children.length === 1 ? y.children[0] : y.children
383
385
  }
384
386
  };
385
- o.length > 0 ? o[o.length - 1].children.push(E) : a.push(E), (m.type === "svg" || m.type === "math") && (u = "HTML");
387
+ o.length > 0 ? o[o.length - 1].children.push(w) : p.push(w), (y.type === "svg" || y.type === "math") && (T = "HTML");
386
388
  }
387
389
  return;
388
390
  }
389
391
  }
390
- function v(s, f, p) {
391
- const T = C(s, u);
392
- if (o.length > 0 && q.has(T)) {
393
- const E = q.get(T), j = o[o.length - 1].type;
394
- E.has(j) && R(j);
392
+ function _(f, c, u) {
393
+ const m = v(f, T);
394
+ if (o.length > 0 && z.has(m)) {
395
+ const w = z.get(m), H = o[o.length - 1].type;
396
+ w.has(H) && I(H);
395
397
  }
396
- let m = u;
397
- if (T === "svg" ? m = "SVG" : T === "math" && (m = "MATHML"), F.has(T) || p) {
398
- const E = {
399
- type: T,
400
- props: f
398
+ let y = T;
399
+ if (m === "svg" ? y = "SVG" : m === "math" && (y = "MATHML"), $.has(m) || u) {
400
+ const w = {
401
+ type: m,
402
+ props: c
401
403
  };
402
- M().push(E);
404
+ x().push(w);
403
405
  } else {
404
- const E = {
405
- type: T,
406
- props: f,
406
+ const w = {
407
+ type: m,
408
+ props: c,
407
409
  children: [],
408
- namespace: m
410
+ namespace: y
409
411
  };
410
- o.push(E), u = m;
412
+ o.push(w), T = y;
411
413
  }
412
414
  }
413
- for (; t < r.length; ) {
414
- const s = r[t];
415
- switch (i) {
415
+ for (; r < s.length; ) {
416
+ const f = s[r];
417
+ switch (n) {
416
418
  case "TEXT":
417
- s === 60 && (X(c, t), i = "TAG_OPEN"), t++;
419
+ f === 60 && (D(h, r), n = "TAG_OPEN"), r++;
418
420
  break;
419
421
  case "TAG_OPEN":
420
- s === 33 ? r[t + 1] === 45 && r[t + 2] === 45 ? (i = "COMMENT", t += 3) : r[t + 1] === 68 || r[t + 1] === 100 ? d(r, t, Math.min(t + 20, r.length)).toLowerCase().startsWith("!doctype") ? (i = "DOCTYPE", y = t, t++) : (c = t - 1, i = "TEXT") : r[t + 1] === 91 && r[t + 2] === 67 && d(r, t, Math.min(t + 9, r.length)).startsWith("![CDATA[") ? (i = "CDATA", c = t + 8, t += 8) : (c = t - 1, i = "TEXT") : s === 63 ? (i = "PROCESSING_INSTRUCTION", y = t - 1, t++) : s === 47 ? (i = "TAG_CLOSE_START", t++) : N(s) ? (y = t, i = "TAG_NAME", h = null, t++) : (c = t - 1, i = "TEXT");
422
+ f === 33 ? s[r + 1] === 45 && s[r + 2] === 45 ? (n = "COMMENT", r += 3) : s[r + 1] === 68 || s[r + 1] === 100 ? b(s, r, Math.min(r + 20, s.length)).toLowerCase().startsWith("!doctype") ? (n = "DOCTYPE", g = r, r++) : (h = r - 1, n = "TEXT") : s[r + 1] === 91 && s[r + 2] === 67 && b(s, r, Math.min(r + 9, s.length)).startsWith("![CDATA[") ? (n = "CDATA", h = r + 8, r += 8) : (h = r - 1, n = "TEXT") : f === 63 ? (n = "PROCESSING_INSTRUCTION", g = r - 1, r++) : f === 47 ? (n = "TAG_CLOSE_START", r++) : S(f) ? (g = r, n = "TAG_NAME", d = null, r++) : (h = r - 1, n = "TEXT");
421
423
  break;
422
424
  case "TAG_NAME":
423
- if (S(s)) {
424
- const f = d(r, y, t), p = f.toLowerCase();
425
- h = {
426
- type: f,
425
+ if (C(f)) {
426
+ const c = b(s, g, r), u = c.toLowerCase();
427
+ d = {
428
+ type: c,
427
429
  props: {},
428
430
  children: [],
429
- namespace: p === "svg" ? "SVG" : p === "math" ? "MATHML" : u
430
- }, i = "ATTRIBUTES", t++;
431
- } else if (s === 62) {
432
- const f = d(r, y, t);
433
- v(f, {}, !1);
434
- const p = C(f, u);
435
- p === "script" ? (i = "SCRIPT_CONTENT", c = t + 1) : p === "style" ? (i = "STYLE_CONTENT", c = t + 1) : (i = "TEXT", c = t + 1), t++;
436
- } else s === 47 ? (i = "TAG_CLOSE_SELF", t++) : N(s) ? t++ : (c = y - 1, i = "TEXT");
431
+ namespace: u === "svg" ? "SVG" : u === "math" ? "MATHML" : T
432
+ }, n = "ATTRIBUTES", r++;
433
+ } else if (f === 62) {
434
+ const c = b(s, g, r);
435
+ _(c, {}, !1);
436
+ const u = v(c, T);
437
+ u === "script" ? (n = "SCRIPT_CONTENT", h = r + 1) : u === "style" ? (n = "STYLE_CONTENT", h = r + 1) : (n = "TEXT", h = r + 1), r++;
438
+ } else f === 47 ? (n = "TAG_CLOSE_SELF", r++) : S(f) ? r++ : (h = g - 1, n = "TEXT");
437
439
  break;
438
440
  case "ATTRIBUTES":
439
- if (S(s))
440
- t++;
441
- else if (s === 62) {
442
- const f = h.type;
443
- v(f, h.props, !1);
444
- const p = C(f, u);
445
- p === "script" ? (i = "SCRIPT_CONTENT", c = t + 1) : p === "style" ? (i = "STYLE_CONTENT", c = t + 1) : (i = "TEXT", c = t + 1), t++;
446
- } else s === 47 ? (i = "TAG_CLOSE_SELF", t++) : N(s) ? (A = t, i = "ATTRIBUTE_NAME", t++) : (c = y - 1, i = "TEXT");
441
+ if (C(f))
442
+ r++;
443
+ else if (f === 62) {
444
+ const c = d.type;
445
+ _(c, d.props, !1);
446
+ const u = v(c, T);
447
+ u === "script" ? (n = "SCRIPT_CONTENT", h = r + 1) : u === "style" ? (n = "STYLE_CONTENT", h = r + 1) : (n = "TEXT", h = r + 1), r++;
448
+ } else f === 47 ? (n = "TAG_CLOSE_SELF", r++) : S(f) ? (E = r, n = "ATTRIBUTE_NAME", r++) : (h = g - 1, n = "TEXT");
447
449
  break;
448
450
  case "ATTRIBUTE_NAME":
449
- if (S(s)) {
450
- g = d(r, A, t);
451
- const f = _(g, h.namespace);
452
- h.props[f] = !0, g = "", i = "ATTRIBUTES", t++;
453
- } else if (s === 61)
454
- g = d(r, A, t), i = "ATTRIBUTE_VALUE_START", t++;
455
- else if (s === 62) {
456
- g = d(r, A, t);
457
- const f = _(g, h.namespace);
458
- h.props[f] = !0, g = "";
459
- const p = h.type;
460
- v(p, h.props, !1);
461
- const T = C(p, u);
462
- T === "script" ? (i = "SCRIPT_CONTENT", c = t + 1) : T === "style" ? (i = "STYLE_CONTENT", c = t + 1) : (i = "TEXT", c = t + 1), t++;
463
- } else if (s === 47) {
464
- g = d(r, A, t);
465
- const f = _(g, h.namespace);
466
- h.props[f] = !0, g = "", i = "TAG_CLOSE_SELF", t++;
467
- } else N(s) ? t++ : (c = y - 1, i = "TEXT");
451
+ if (C(f)) {
452
+ A = b(s, E, r);
453
+ const c = L(A, d.namespace);
454
+ d.props[c] = !0, A = "", n = "ATTRIBUTES", r++;
455
+ } else if (f === 61)
456
+ A = b(s, E, r), n = "ATTRIBUTE_VALUE_START", r++;
457
+ else if (f === 62) {
458
+ A = b(s, E, r);
459
+ const c = L(A, d.namespace);
460
+ d.props[c] = !0, A = "";
461
+ const u = d.type;
462
+ _(u, d.props, !1);
463
+ const m = v(u, T);
464
+ m === "script" ? (n = "SCRIPT_CONTENT", h = r + 1) : m === "style" ? (n = "STYLE_CONTENT", h = r + 1) : (n = "TEXT", h = r + 1), r++;
465
+ } else if (f === 47) {
466
+ A = b(s, E, r);
467
+ const c = L(A, d.namespace);
468
+ d.props[c] = !0, A = "", n = "TAG_CLOSE_SELF", r++;
469
+ } else S(f) ? r++ : (h = g - 1, n = "TEXT");
468
470
  break;
469
471
  case "ATTRIBUTE_VALUE_START":
470
- S(s) ? t++ : s === 34 || s === 39 ? (P = s, b = t + 1, i = "ATTRIBUTE_VALUE_QUOTED", t++) : (b = t, i = "ATTRIBUTE_VALUE_UNQUOTED");
472
+ C(f) ? r++ : f === 34 || f === 39 ? (q = f, N = r + 1, n = "ATTRIBUTE_VALUE_QUOTED", r++) : (N = r, n = "ATTRIBUTE_VALUE_UNQUOTED");
471
473
  break;
472
474
  case "ATTRIBUTE_VALUE_QUOTED":
473
- if (s === P) {
474
- const f = d(r, b, t), p = w(f), T = _(g, h.namespace);
475
- T === "style" ? h.props[T] = D(p) : h.props[T] = p, g = "", i = "ATTRIBUTES", t++;
475
+ if (f === q) {
476
+ const c = b(s, N, r), u = O(c), m = L(A, d.namespace);
477
+ m === "style" ? d.props[m] = F(u) : d.props[m] = u, A = "", n = "ATTRIBUTES", r++;
476
478
  } else
477
- t++;
479
+ r++;
478
480
  break;
479
481
  case "ATTRIBUTE_VALUE_UNQUOTED":
480
- if (S(s) || s === 62 || s === 47) {
481
- const f = d(r, b, t), p = w(f), T = _(g, h.namespace);
482
- T === "style" ? h.props[T] = D(p) : h.props[T] = p, g = "", i = "ATTRIBUTES";
482
+ if (C(f) || f === 62 || f === 47) {
483
+ const c = b(s, N, r), u = O(c), m = L(A, d.namespace);
484
+ m === "style" ? d.props[m] = F(u) : d.props[m] = u, A = "", n = "ATTRIBUTES";
483
485
  } else
484
- t++;
486
+ r++;
485
487
  break;
486
488
  case "TAG_CLOSE_SELF":
487
- if (s === 62) {
488
- const f = h.type;
489
- v(f, h.props, !0), i = "TEXT", c = t + 1, t++;
489
+ if (f === 62) {
490
+ const c = d.type;
491
+ _(c, d.props, !0), n = "TEXT", h = r + 1, r++;
490
492
  } else
491
- c = y - 1, i = "TEXT";
493
+ h = g - 1, n = "TEXT";
492
494
  break;
493
495
  case "TAG_CLOSE_START":
494
- N(s) ? (y = t, i = "TAG_CLOSE_NAME", t++) : (c = t - 2, i = "TEXT");
496
+ S(f) ? (g = r, n = "TAG_CLOSE_NAME", r++) : (h = r - 2, n = "TEXT");
495
497
  break;
496
498
  case "TAG_CLOSE_NAME":
497
- if (s === 62) {
498
- const f = d(r, y, t);
499
- R(f), i = "TEXT", c = t + 1, t++;
500
- } else S(s) || N(s) ? t++ : (c = y - 2, i = "TEXT");
499
+ if (f === 62) {
500
+ const c = b(s, g, r);
501
+ I(c), n = "TEXT", h = r + 1, r++;
502
+ } else C(f) || S(f) ? r++ : (h = g - 2, n = "TEXT");
501
503
  break;
502
504
  case "COMMENT":
503
- s === 45 && r[t + 1] === 45 && r[t + 2] === 62 ? (i = "TEXT", c = t + 3, t += 3) : t++;
505
+ f === 45 && s[r + 1] === 45 && s[r + 2] === 62 ? (n = "TEXT", h = r + 3, r += 3) : r++;
504
506
  break;
505
507
  case "DOCTYPE":
506
- if (s === 62) {
507
- const f = d(r, y, t + 1);
508
- l.doctype = "<" + f, i = "TEXT", c = t + 1, t++;
508
+ if (f === 62) {
509
+ const c = b(s, g, r + 1);
510
+ a.doctype = "<" + c, n = "TEXT", h = r + 1, r++;
509
511
  } else
510
- t++;
512
+ r++;
511
513
  break;
512
514
  case "CDATA":
513
- s === 93 && r[t + 1] === 93 && r[t + 2] === 62 ? (U(c, t), i = "TEXT", c = t + 3, t += 3) : t++;
515
+ f === 93 && s[r + 1] === 93 && s[r + 2] === 62 ? (M(h, r), n = "TEXT", h = r + 3, r += 3) : r++;
514
516
  break;
515
517
  case "PROCESSING_INSTRUCTION":
516
- if (s === 63 && r[t + 1] === 62) {
517
- const f = d(r, y, t + 2);
518
- l.xml = f, i = "TEXT", c = t + 2, t += 2;
518
+ if (f === 63 && s[r + 1] === 62) {
519
+ const c = b(s, g, r + 2);
520
+ a.xml = c, n = "TEXT", h = r + 2, r += 2;
519
521
  } else
520
- t++;
522
+ r++;
521
523
  break;
522
524
  case "SCRIPT_CONTENT":
523
525
  case "STYLE_CONTENT":
524
- if (s === 60 && r[t + 1] === 47) {
525
- const f = i === "SCRIPT_CONTENT" ? "script" : "style", p = "</" + f, T = d(r, t, Math.min(t + p.length + 1, r.length)).toLowerCase();
526
- if (T.startsWith(p) && (T[p.length] === ">" || S(T.charCodeAt(p.length)))) {
527
- U(c, t);
528
- let m = t + p.length;
529
- for (; m < r.length && r[m] !== 62; ) m++;
530
- R(f), i = "TEXT", c = m + 1, t = m + 1;
526
+ if (f === 60 && s[r + 1] === 47) {
527
+ const c = n === "SCRIPT_CONTENT" ? "script" : "style", u = "</" + c, m = b(s, r, Math.min(r + u.length + 1, s.length)).toLowerCase();
528
+ if (m.startsWith(u) && (m[u.length] === ">" || C(m.charCodeAt(u.length)))) {
529
+ M(h, r);
530
+ let y = r + u.length;
531
+ for (; y < s.length && s[y] !== 62; ) y++;
532
+ I(c), n = "TEXT", h = y + 1, r = y + 1;
531
533
  } else
532
- t++;
534
+ r++;
533
535
  } else
534
- t++;
536
+ r++;
535
537
  break;
536
538
  default:
537
- t++;
539
+ r++;
538
540
  }
539
541
  }
540
- for (i === "TEXT" ? X(c, t) : (i === "SCRIPT_CONTENT" || i === "STYLE_CONTENT") && U(c, t); o.length > 0; ) {
541
- const s = o.pop(), f = {
542
- type: s.type,
542
+ for (n === "TEXT" ? D(h, r) : (n === "SCRIPT_CONTENT" || n === "STYLE_CONTENT") && M(h, r); o.length > 0; ) {
543
+ const f = o.pop(), c = {
544
+ type: f.type,
543
545
  props: {
544
- ...s.props,
545
- children: s.children.length === 1 ? s.children[0] : s.children
546
+ ...f.props,
547
+ children: f.children.length === 1 ? f.children[0] : f.children
546
548
  }
547
549
  };
548
- o.length > 0 ? o[o.length - 1].children.push(f) : a.push(f);
550
+ o.length > 0 ? o[o.length - 1].children.push(c) : p.push(c);
549
551
  }
550
- return a.length === 0 ? l.node = null : a.length === 1 ? l.node = a[0] : l.node = a, l;
551
- }
552
- function D(e) {
553
- const n = {}, l = w(e).split(/;(?![^(]*\))/);
554
- for (const a of l) {
555
- const o = a.indexOf(":");
556
- if (o === -1) continue;
557
- const u = a.substring(0, o).trim(), i = a.substring(o + 1).trim();
558
- u && i && (n[k(u)] = i);
552
+ return p.length === 0 ? a.node = null : p.length === 1 ? a.node = p[0] : a.node = p, a;
553
+ }
554
+ function F(e) {
555
+ const t = {}, l = O(e).split(/;(?![^(]*\))/);
556
+ for (const s of l) {
557
+ const a = s.indexOf(":");
558
+ if (a === -1) continue;
559
+ const p = s.substring(0, a).trim(), o = s.substring(a + 1).trim();
560
+ p && o && (t[Z(p)] = o);
559
561
  }
560
- return n;
562
+ return t;
561
563
  }
562
- function H(e, n = {}) {
563
- const r = [];
564
- let l, a, o;
564
+ const re = {
565
+ className: "class",
566
+ htmlFor: "for"
567
+ };
568
+ function se(e, t) {
569
+ return t === "reactName" ? e : t === "exactName" || t === "eitherName" ? re[e] ?? e : e;
570
+ }
571
+ function k(e, t = {}) {
572
+ const i = [];
573
+ let l, s, a;
565
574
  if (e && typeof e == "object" && "node" in e) {
566
- const c = e;
567
- l = c.node, a = c.xml ?? n.xml, o = c.doctype ?? n.doctype;
575
+ const r = e;
576
+ l = r.node, s = r.xml ?? t.xml, a = r.doctype ?? t.doctype;
568
577
  } else
569
- l = e, a = n.xml, o = n.doctype;
570
- a && (r.push(a), a.endsWith(`
571
- `) || r.push(`
572
- `)), o && (r.push(o), o.endsWith(`
573
- `) || r.push(`
578
+ l = e, s = t.xml, a = t.doctype;
579
+ s && (i.push(s), s.endsWith(`
580
+ `) || i.push(`
581
+ `)), a && (i.push(a), a.endsWith(`
582
+ `) || i.push(`
574
583
  `));
575
- const u = n.useCDataForScripts ?? !1, i = n.useCDataForStyles ?? !1, t = n.voidTrailingSlash ?? !0;
576
- return O(l, r, "HTML", u, i, t), r.join("");
584
+ const p = t.useCDataForScripts ?? !1, o = t.useCDataForStyles ?? !1, T = t.voidTrailingSlash ?? !0, n = t.attributeNaming ?? "eitherName";
585
+ return R(l, i, "HTML", p, o, T, n), i.join("");
577
586
  }
578
- function O(e, n, r, l, a, o) {
587
+ function R(e, t, i, l, s, a, p) {
579
588
  if (e != null) {
580
589
  if (typeof e == "string") {
581
- n.push(x(e, !1));
590
+ t.push(G(e, !1));
582
591
  return;
583
592
  }
584
593
  if (typeof e == "number" || typeof e == "bigint") {
585
- n.push(String(e));
594
+ t.push(String(e));
586
595
  return;
587
596
  }
588
597
  if (typeof e == "boolean") {
589
- n.push(String(e));
598
+ t.push(String(e));
590
599
  return;
591
600
  }
592
601
  if (Array.isArray(e)) {
593
- for (const u of e)
594
- O(u, n, r, l, a, o);
602
+ for (const o of e)
603
+ R(o, t, i, l, s, a, p);
595
604
  return;
596
605
  }
597
606
  if (typeof e != "function" && typeof e != "symbol" && !(e && typeof e == "object" && "then" in e && typeof e.then == "function") && e && typeof e == "object" && "type" in e && "props" in e) {
598
- J(e, n, r, l, a, o);
607
+ ie(e, t, i, l, s, a, p);
599
608
  return;
600
609
  }
601
610
  }
602
611
  }
603
- function J(e, n, r, l, a, o) {
604
- var y;
605
- const u = e.type;
606
- let i = r;
607
- if (u === "svg" ? i = "SVG" : u === "math" && (i = "MATHML"), n.push("<"), n.push(u), e.props) {
608
- for (const [A, b] of Object.entries(e.props))
609
- if (A !== "children" && !(b === !1 || b === null || b === void 0))
610
- if (b === !0)
611
- n.push(" "), n.push(A);
612
- else if (A === "style" && typeof b == "object") {
613
- const g = ee(b);
614
- g && (n.push(' style="'), n.push(x(g, !0)), n.push('"'));
615
- } else
616
- n.push(" "), n.push(A), n.push('="'), n.push(x(String(b), !0)), n.push('"');
617
- }
618
- const t = (y = e.props) == null ? void 0 : y.children;
619
- let c;
620
- if (t && typeof t == "object" && "then" in t && typeof t.then == "function" ? c = !1 : c = t != null && (Array.isArray(t) && t.length > 0 || !Array.isArray(t) && t !== !1), F.has(u))
621
- o ? n.push(" />") : n.push(">");
622
- else if (c) {
623
- n.push(">");
624
- const A = t;
625
- u === "script" && l || u === "style" && a ? (n.push("<![CDATA["), G(A, n, i, l, a, o), n.push("]]>")) : u === "script" || u === "style" ? W(A, n) : O(A, n, i, l, a, o), n.push("</"), n.push(u), n.push(">");
626
- } else Q.has(u), n.push("></"), n.push(u), n.push(">");
627
- }
628
- function G(e, n, r, l, a, o, u) {
612
+ function ie(e, t, i, l, s, a, p) {
613
+ var h;
614
+ const o = e.type;
615
+ let T = i;
616
+ if (o === "svg" ? T = "SVG" : o === "math" && (T = "MATHML"), t.push("<"), t.push(o), e.props)
617
+ for (const [g, E] of Object.entries(e.props)) {
618
+ if (g === "children" || E === !1 || E === null || E === void 0)
619
+ continue;
620
+ const N = se(g, p);
621
+ if (E === !0)
622
+ t.push(" "), t.push(N);
623
+ else if (g === "style" && typeof E == "object") {
624
+ const A = ne(E);
625
+ A && (t.push(' style="'), t.push(G(A, !0)), t.push('"'));
626
+ } else
627
+ t.push(" "), t.push(N), t.push('="'), t.push(G(String(E), !0)), t.push('"');
628
+ }
629
+ const n = (h = e.props) == null ? void 0 : h.children;
630
+ let r;
631
+ if (n && typeof n == "object" && "then" in n && typeof n.then == "function" ? r = !1 : r = n != null && (Array.isArray(n) && n.length > 0 || !Array.isArray(n) && n !== !1), $.has(o))
632
+ a ? t.push(" />") : t.push(">");
633
+ else if (r) {
634
+ t.push(">");
635
+ const g = n;
636
+ o === "script" && l || o === "style" && s ? (t.push("<![CDATA["), j(g, t, T, l, s, a, p), t.push("]]>")) : o === "script" || o === "style" ? K(g, t) : R(g, t, T, l, s, a, p), t.push("</"), t.push(o), t.push(">");
637
+ } else J.has(o), t.push("></"), t.push(o), t.push(">");
638
+ }
639
+ function j(e, t, i, l, s, a, p, o) {
629
640
  if (e != null) {
630
641
  if (typeof e == "string") {
631
- n.push(Z(e));
642
+ t.push(ee(e));
632
643
  return;
633
644
  }
634
645
  if (typeof e == "number" || typeof e == "bigint" || typeof e == "boolean") {
635
- n.push(String(e));
646
+ t.push(String(e));
636
647
  return;
637
648
  }
638
649
  if (Array.isArray(e)) {
639
- for (const i of e)
640
- G(i, n, r, l, a, o);
650
+ for (const T of e)
651
+ j(T, t, i, l, s, a, p);
641
652
  return;
642
653
  }
643
- O(e, n, r, l, a, o);
654
+ R(e, t, i, l, s, a, p);
644
655
  }
645
656
  }
646
- function W(e, n) {
657
+ function K(e, t) {
647
658
  if (e != null) {
648
659
  if (typeof e == "string") {
649
- n.push(e);
660
+ t.push(e);
650
661
  return;
651
662
  }
652
663
  if (typeof e == "number" || typeof e == "bigint" || typeof e == "boolean") {
653
- n.push(String(e));
664
+ t.push(String(e));
654
665
  return;
655
666
  }
656
667
  if (Array.isArray(e)) {
657
- for (const r of e)
658
- W(r, n);
668
+ for (const i of e)
669
+ K(i, t);
659
670
  return;
660
671
  }
661
672
  }
662
673
  }
663
- function ee(e) {
664
- const n = [];
665
- for (const [r, l] of Object.entries(e))
674
+ function ne(e) {
675
+ const t = [];
676
+ for (const [i, l] of Object.entries(e))
666
677
  if (l) {
667
- const a = $(r);
668
- n.push(`${a}: ${l}`);
678
+ const s = te(i);
679
+ t.push(`${s}: ${l}`);
669
680
  }
670
- return n.join("; ");
681
+ return t.join("; ");
671
682
  }
672
- function te(e) {
673
- const n = [];
674
- function r(l) {
675
- if (typeof l == "string" && n.push(l), (typeof l == "number" || typeof l == "bigint" || typeof l == "boolean") && n.push(`${l}`), typeof l == "object" && l !== null && "type" in l && l.props && "children" in l.props) {
676
- const a = l.props.children;
677
- if (l.type === "script" || l.type === "style" || l.type === "template" || a && typeof a == "object" && "then" in a && typeof a.then == "function")
683
+ function be(e) {
684
+ const t = [];
685
+ function i(l) {
686
+ if (typeof l == "string" && t.push(l), (typeof l == "number" || typeof l == "bigint" || typeof l == "boolean") && t.push(`${l}`), typeof l == "object" && l !== null && "type" in l && l.props && "children" in l.props) {
687
+ const s = l.props.children;
688
+ if (l.type === "script" || l.type === "style" || l.type === "template" || s && typeof s == "object" && "then" in s && typeof s.then == "function")
678
689
  return;
679
- if (Array.isArray(a))
680
- for (const o of a)
681
- r(o);
682
- else a != null && r(a);
690
+ if (Array.isArray(s))
691
+ for (const a of s)
692
+ i(a);
693
+ else s != null && i(s);
683
694
  }
684
695
  if (Array.isArray(l))
685
- for (const a of l)
686
- r(a);
696
+ for (const s of l)
697
+ i(s);
687
698
  }
688
- return r(e), n.join("");
699
+ return i(e), t.join("");
689
700
  }
690
- async function L(e) {
701
+ async function U(e) {
691
702
  if (e == null)
692
703
  return e;
693
704
  if (e && typeof e == "object" && "then" in e && typeof e.then == "function") {
694
- const n = await e;
695
- return L(n);
705
+ const t = await e;
706
+ return U(t);
696
707
  }
697
708
  if (typeof e == "string" || typeof e == "number" || typeof e == "boolean" || typeof e == "bigint")
698
709
  return e;
699
710
  if (typeof e != "function" && typeof e != "symbol") {
700
711
  if (Array.isArray(e))
701
712
  return await Promise.all(
702
- e.filter((r) => !(r == null || typeof r == "function" || typeof r == "symbol")).map((r) => L(r))
713
+ e.filter((i) => !(i == null || typeof i == "function" || typeof i == "symbol")).map((i) => U(i))
703
714
  );
704
715
  if (e && typeof e == "object" && "type" in e && "props" in e) {
705
- const n = e, r = {};
706
- for (const [l, a] of Object.entries(n.props))
716
+ const t = e, i = {};
717
+ for (const [l, s] of Object.entries(t.props))
707
718
  if (l === "children") {
708
- const o = await L(a);
709
- o != null && (r.children = o);
719
+ const a = await U(s);
720
+ a != null && (i.children = a);
710
721
  } else {
711
- if (typeof a == "function" || typeof a == "symbol")
722
+ if (typeof s == "function" || typeof s == "symbol")
712
723
  continue;
713
- a != null && (r[l] = a);
724
+ s != null && (i[l] = s);
714
725
  }
715
726
  return {
716
- type: n.type,
717
- props: r
727
+ type: t.type,
728
+ props: i
718
729
  };
719
730
  }
720
731
  }
721
732
  }
722
- function Y(e, n) {
733
+ const le = [
734
+ "br",
735
+ ["span", "class"],
736
+ "p",
737
+ ["a", "href", "rel", "class"],
738
+ "pre",
739
+ "del",
740
+ "code",
741
+ "em",
742
+ "strong",
743
+ "i",
744
+ "u",
745
+ "ul",
746
+ ["ol", "start", "reversed"],
747
+ ["li", "value"],
748
+ "blockquote"
749
+ ], ae = [
750
+ "h-*",
751
+ "p-*",
752
+ "u-*",
753
+ "dt-*",
754
+ "e-*",
755
+ "mention",
756
+ "hashtag",
757
+ "ellipsis",
758
+ "invisible"
759
+ ], oe = ["http", "https"], fe = {
760
+ a: ["href"],
761
+ area: ["href"],
762
+ audio: ["src"],
763
+ base: ["href"],
764
+ blockquote: ["cite"],
765
+ button: ["formaction"],
766
+ del: ["cite"],
767
+ embed: ["src"],
768
+ form: ["action"],
769
+ iframe: ["src"],
770
+ img: ["src", "longdesc"],
771
+ input: ["src", "formaction"],
772
+ ins: ["cite"],
773
+ link: ["href"],
774
+ object: ["data"],
775
+ q: ["cite"],
776
+ script: ["src"],
777
+ source: ["src"],
778
+ track: ["src"],
779
+ video: ["src", "poster"]
780
+ };
781
+ function ce(e) {
782
+ const t = /* @__PURE__ */ new Map();
783
+ let i = !1, l = /* @__PURE__ */ new Set(), s = !1;
784
+ for (const a of e)
785
+ if (typeof a == "string")
786
+ a === "*" ? i = !0 : t.set(a.toLowerCase(), {
787
+ allowed: !0,
788
+ allowedAttributes: /* @__PURE__ */ new Set(),
789
+ allowAllAttributes: !1
790
+ });
791
+ else {
792
+ const [p, ...o] = a, T = o.includes("*");
793
+ p === "*" ? (i = !0, T ? s = !0 : l = new Set(o.map((n) => n.toLowerCase()))) : t.set(p.toLowerCase(), {
794
+ allowed: !0,
795
+ allowedAttributes: T ? /* @__PURE__ */ new Set() : new Set(o.map((n) => n.toLowerCase())),
796
+ allowAllAttributes: T
797
+ });
798
+ }
799
+ return { configs: t, allowAll: i, universalAttrs: l, universalAllowAllAttrs: s };
800
+ }
801
+ function ue(e) {
802
+ return e.includes("*") ? { patterns: [], allowAll: !0 } : { patterns: e.map((i) => {
803
+ const s = "^" + i.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
804
+ return new RegExp(s);
805
+ }), allowAll: !1 };
806
+ }
807
+ function pe(e = {}) {
808
+ const t = e.allowedTags ?? le, i = e.allowedClasses ?? ae, l = e.allowedLinkProtocols ?? oe, s = e.attributeNaming ?? "reactName", a = ce(t), p = ue(i);
809
+ return {
810
+ tagConfigs: a.configs,
811
+ classPatterns: p.patterns,
812
+ protocols: new Set(l.map((o) => o.toLowerCase())),
813
+ attributeNaming: s,
814
+ allowAllClasses: p.allowAll,
815
+ allowAllTags: a.allowAll,
816
+ universalAttributes: a.universalAttrs,
817
+ universalAllowAllAttributes: a.universalAllowAllAttrs
818
+ };
819
+ }
820
+ function he(e, t, i) {
821
+ return i ? !0 : t.some((l) => l.test(e));
822
+ }
823
+ function me(e, t, i) {
824
+ return i ? e : e.split(/\s+/).filter((a) => a.length > 0).filter((a) => he(a, t, i)).join(" ");
825
+ }
826
+ function Te(e, t) {
827
+ const i = e.trim();
828
+ if (t.has(".")) {
829
+ if (i.startsWith("/") || i.startsWith("./") || i.startsWith("../") || i.startsWith("#") || i.startsWith("?"))
830
+ return !0;
831
+ const a = i.indexOf(":"), p = i.indexOf("/");
832
+ if (a === -1 || p !== -1 && p < a)
833
+ return !0;
834
+ }
835
+ const l = i.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
836
+ if (!l || !l[1])
837
+ return t.has(".");
838
+ const s = l[1].toLowerCase();
839
+ return t.has(s);
840
+ }
841
+ function ge(e) {
842
+ return fe[e.toLowerCase()] ?? [];
843
+ }
844
+ function de(e, t, i, l) {
845
+ const s = {}, a = ge(e), p = i.allowAllAttributes || l.universalAllowAllAttributes, o = l.attributeNaming === "exactName" ? "class" : "className", T = l.attributeNaming === "exactName" ? "for" : "htmlFor";
846
+ for (const [n, r] of Object.entries(t)) {
847
+ if (n === "children")
848
+ continue;
849
+ const h = n.toLowerCase();
850
+ if (a.includes(h) && typeof r == "string" && !Te(r, l.protocols))
851
+ return null;
852
+ if ((n === "className" || n === "class") && typeof r == "string") {
853
+ if (p)
854
+ s[o] = r;
855
+ else if (i.allowedAttributes.has("class") || l.universalAttributes.has("class")) {
856
+ const g = me(r, l.classPatterns, l.allowAllClasses);
857
+ g && (s[o] = g);
858
+ }
859
+ continue;
860
+ }
861
+ if ((n === "htmlFor" || n === "for") && typeof r == "string") {
862
+ (p || i.allowedAttributes.has("for") || l.universalAttributes.has("for")) && (s[T] = r);
863
+ continue;
864
+ }
865
+ (p || i.allowedAttributes.has(h) || i.allowedAttributes.has(n) || l.universalAttributes.has(h) || l.universalAttributes.has(n)) && (s[n] = r);
866
+ }
867
+ return s;
868
+ }
869
+ function X(e, t) {
870
+ if (e == null || typeof e == "string" || typeof e == "number" || typeof e == "boolean" || typeof e == "bigint")
871
+ return e;
872
+ if (Array.isArray(e)) {
873
+ const o = [];
874
+ for (const T of e) {
875
+ const n = X(T, t);
876
+ Array.isArray(n) ? o.push(...n) : n != null && o.push(n);
877
+ }
878
+ return o.length === 0 ? [] : o.length === 1 ? o[0] : o;
879
+ }
880
+ const i = e, l = i.type.toLowerCase();
881
+ let s = t.tagConfigs.get(l);
882
+ !s && t.allowAllTags && (s = {
883
+ allowed: !0,
884
+ allowedAttributes: t.universalAttributes,
885
+ allowAllAttributes: t.universalAllowAllAttributes
886
+ });
887
+ const a = i.props.children !== void 0 ? X(i.props.children, t) : void 0;
888
+ if (!s) {
889
+ if (l === "img") {
890
+ const o = i.props.alt;
891
+ return typeof o == "string" && o.length > 0 ? o : void 0;
892
+ }
893
+ return a !== void 0 ? a : void 0;
894
+ }
895
+ const p = de(l, i.props, s, t);
896
+ if (p === null) {
897
+ if (l === "img") {
898
+ const o = i.props.alt;
899
+ if (typeof o == "string" && o.length > 0)
900
+ return o;
901
+ }
902
+ return a !== void 0 ? a : void 0;
903
+ }
904
+ return a !== void 0 && (p.children = a), {
905
+ type: i.type,
906
+ props: p
907
+ };
908
+ }
909
+ function ye(e, t = {}) {
910
+ const i = pe(t);
911
+ return X(e, i);
912
+ }
913
+ function Y(e, t) {
723
914
  if (e == null || typeof e == "string" || typeof e == "number" || typeof e == "boolean" || typeof e == "bigint")
724
915
  return e;
725
916
  if (Array.isArray(e))
726
- return e.map((r) => Y(r, n));
917
+ return e.map((i) => Y(i, t));
727
918
  if (typeof e == "object" && "type" in e && "props" in e) {
728
- const { type: r, props: l } = e, { children: a, ...o } = l;
729
- if (a !== void 0) {
730
- const u = Y(a, n);
731
- return u === null ? n(r, o) : Array.isArray(u) ? n(r, o, ...u) : n(r, o, u);
919
+ const { type: i, props: l } = e, { children: s, ...a } = l;
920
+ if (s !== void 0) {
921
+ const p = Y(s, t);
922
+ return p === null ? t(i, a) : Array.isArray(p) ? t(i, a, ...p) : t(i, a, p);
732
923
  }
733
- return n(r, o);
924
+ return t(i, a);
734
925
  }
735
926
  return null;
736
927
  }
737
- function re(e) {
738
- return K(e);
928
+ function Ee(e, t = {}) {
929
+ return Q(e, t);
930
+ }
931
+ function we(e, t = {}) {
932
+ return e && typeof e == "object" && "node" in e ? k(e, t) : e === void 0 ? "" : k(e, t);
739
933
  }
740
- function ne(e, n = {}) {
741
- return e && typeof e == "object" && "node" in e ? H(e, n) : e === void 0 ? "" : H(e, n);
934
+ async function Ne(e) {
935
+ return await U(e);
742
936
  }
743
- async function ie(e) {
744
- return await L(e);
937
+ function Ce(e, t = {}) {
938
+ return ye(e, t);
745
939
  }
746
940
  export {
747
- ie as awaitHtmlNode,
748
- w as decodeHtmlEntities,
749
- x as encodeHtmlEntities,
750
- te as getTextContent,
941
+ Ne as awaitHtmlNode,
942
+ O as decodeHtmlEntities,
943
+ G as encodeHtmlEntities,
944
+ be as getTextContent,
751
945
  Y as htmlNodeTo,
752
- re as readHtml,
753
- ne as writeHtml
946
+ Ae as htmlNodeToHtmlElement,
947
+ Ee as readHtml,
948
+ Ce as safeHtml,
949
+ we as writeHtml
754
950
  };
package/dist/parser.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { ParseResult } from './types.ts';
2
- export declare function parseHtml(html: string): ParseResult;
1
+ import { ParseResult, ParserOptions } from './types.ts';
2
+ export declare function parseHtml(html: string, options?: ParserOptions): ParseResult;
@@ -0,0 +1,15 @@
1
+ import { HtmlNode, SafeHtmlOptions } from './types.ts';
2
+ /**
3
+ * Sanitizes an HtmlNode tree by removing disallowed tags, attributes, and URLs.
4
+ *
5
+ * - Tags not in allowedTags have their content retained (folded into parent)
6
+ * - Images replaced with their alt text when removed
7
+ * - Attributes not in the tag's allowed list are dropped
8
+ * - Classes are filtered by allowedClasses patterns (supports wildcards like 'h-*')
9
+ * - URLs not matching allowedLinkProtocols cause the element to be removed
10
+ * - Use '.' as a protocol to allow relative URLs
11
+ * @param node - The HtmlNode to sanitize
12
+ * @param options - The SafeHtmlOptions to use
13
+ * @returns The sanitized HtmlNode
14
+ */
15
+ export declare function safeHtml(node: HtmlNode, options?: SafeHtmlOptions): HtmlNode;
package/dist/types.d.ts CHANGED
@@ -1,14 +1,26 @@
1
+ /**
2
+ * Style attribute as a structured record.
3
+ */
1
4
  export interface HtmlStyle {
2
5
  [key: string]: string;
3
6
  }
7
+ /**
8
+ * Properties of an HtmlElement.
9
+ */
4
10
  export interface HtmlElement {
5
11
  type: string;
6
12
  props: HtmlProps;
7
13
  }
14
+ /**
15
+ * HtmlElement properties
16
+ */
8
17
  export interface HtmlProps {
9
18
  [key: string]: string | number | boolean | HtmlStyle | HtmlNode | Promise<HtmlNode>;
10
19
  children?: HtmlNode | Promise<HtmlNode>;
11
20
  }
21
+ /**
22
+ * Nodes will be parsed into one of these types.
23
+ */
12
24
  export type HtmlNode = HtmlElement | string | number | bigint | boolean | null | undefined | HtmlNode[];
13
25
  export declare enum ParserState {
14
26
  TEXT = "TEXT",
@@ -29,6 +41,9 @@ export declare enum ParserState {
29
41
  SCRIPT_CONTENT = "SCRIPT_CONTENT",
30
42
  STYLE_CONTENT = "STYLE_CONTENT"
31
43
  }
44
+ /**
45
+ * Namespace of an element.
46
+ */
32
47
  export declare enum Namespace {
33
48
  HTML = "HTML",
34
49
  SVG = "SVG",
@@ -45,10 +60,71 @@ export interface ParseResult {
45
60
  doctype?: string;
46
61
  node: HtmlNode | HtmlNode[];
47
62
  }
63
+ /**
64
+ * Controls how special attributes like 'class' and 'for' are named:
65
+ * - 'reactName': Use React-style names (className, htmlFor) - default for parsing
66
+ * - 'exactName': Use exact HTML names (class, for)
67
+ */
68
+ export type ParserAttributeNaming = 'reactName' | 'exactName';
69
+ /**
70
+ * Controls how special attributes are written:
71
+ * - 'reactName': Write as React-style (className, htmlFor)
72
+ * - 'exactName': Write as exact HTML (class, for)
73
+ * - 'eitherName': Normalize className->class, htmlFor->for (default)
74
+ */
75
+ export type WriterAttributeNaming = 'reactName' | 'exactName' | 'eitherName';
76
+ export interface ParserOptions {
77
+ /**
78
+ * How to name special attributes like 'class' and 'for'.
79
+ * - 'reactName' (default): Convert to React-style (className, htmlFor)
80
+ * - 'exactName': Keep exact HTML names (class, for)
81
+ */
82
+ attributeNaming?: ParserAttributeNaming;
83
+ }
48
84
  export interface WriterOptions {
49
85
  useCDataForScripts?: boolean;
50
86
  useCDataForStyles?: boolean;
51
87
  xml?: string;
52
88
  doctype?: string;
53
89
  voidTrailingSlash?: boolean;
90
+ /**
91
+ * How to write special attributes like 'class' and 'for'.
92
+ * - 'eitherName' (default): Normalize className->class, htmlFor->for
93
+ * - 'reactName': Write as-is (className, htmlFor)
94
+ * - 'exactName': Write as exact HTML (class, for)
95
+ */
96
+ attributeNaming?: WriterAttributeNaming;
97
+ }
98
+ /**
99
+ * Allowed tag specification:
100
+ * - string: tag name with no custom attributes allowed (only global attrs like 'class' if classes are allowed)
101
+ * - [tagName, ...attrs]: tag name with specific allowed attributes
102
+ */
103
+ export type AllowedTag = string | [string, ...string[]];
104
+ /**
105
+ * Options for the safeHtml function.
106
+ */
107
+ export interface SafeHtmlOptions {
108
+ /**
109
+ * List of allowed tags. Can be strings (tag name only) or tuples [tagName, ...allowedAttributes].
110
+ * If not provided, uses default safe tags.
111
+ */
112
+ allowedTags?: AllowedTag[];
113
+ /**
114
+ * List of allowed CSS class patterns. Supports wildcards like 'h-*', 'p-*'.
115
+ * If not provided, uses default allowed classes.
116
+ */
117
+ allowedClasses?: string[];
118
+ /**
119
+ * List of allowed URL protocols for links/media. Use '.' for relative URLs.
120
+ * If not provided, uses default ['http', 'https'].
121
+ */
122
+ allowedLinkProtocols?: string[];
123
+ /**
124
+ * The attribute naming convention used in the input.
125
+ * - 'reactName' (default): Input uses React-style names (className, htmlFor)
126
+ * - 'exactName': Input uses exact HTML names (class, for)
127
+ * When set, also affects how the output is named.
128
+ */
129
+ attributeNaming?: ParserAttributeNaming;
54
130
  }
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { HtmlElement, HtmlNode } from './types.ts';
1
2
  /**
2
3
  * Converts a kebab-case or snake_case string to camelCase
3
4
  * @example toCamelCase("foo-bar") => "fooBar"
@@ -32,3 +33,11 @@ export declare function isWhitespace(code: number): boolean;
32
33
  * Checks if a character code is valid for tag/attribute names
33
34
  */
34
35
  export declare function isNameChar(code: number): boolean;
36
+ /**
37
+ * Find the first HtmlElement in the HtmlNode, may be an html string.
38
+ *
39
+ * @param node - the result of readHtml or a string of HTML
40
+ * @returns the first HtmlElement in the HtmlNode
41
+ * @throws an error if no HtmlElement is found
42
+ */
43
+ export declare function htmlNodeToHtmlElement(node: HtmlNode): HtmlElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levischuck/tiny-html",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",